package com.trg.flex
{
	import mx.rpc.IResponder;

	/**
	 * The Callback object is used throughout the framework as a simple and versatile way to handle asynchronous calls.
	 */
	public class Callback implements IResponder
	{
		
		/**
		 * This constructor allows the onSuccess, onFailure and/or data properties to be set inline. Any number of additional Callbacks or IResponders may be specified. Each of these is also called when an outcome of this callback is called.
		 */
		public function Callback(onSuccess:Function=null, onFailure:Function=null, data:*=null, ...extraCallbacks:Array)
		{
			this.onSuccess = onSuccess;
			this.onFailure = onFailure;
			this.data = data;
			if (extraCallbacks.length == 1 && extraCallbacks[0] is Array) extraCallbacks = extraCallbacks[0];
			for each (var callback:Callback in extraCallbacks) {
				addCallback(callback);
			}
		}
		
		/**
		 * The function to call when the asynchronous operation completes successfully.
		 * It may have zero, one or two parameters. If it does accept one or two parameters,
		 * the first is the result data passed back from the operation. If it accepts two
		 * parameters, the second is the data stored on this callback in the data property.
		 */
		public var onSuccess:Function;
		
		/**
		 * The function to call if and when the asynchronous operation fails. It may have
		 * one or two parameters. The first is the error passed back from the operation. If
		 * it accepts two parameters, the second is the data stored on this callback in the data property.
		 */
		public var onFailure:Function;
		
		/**
		 * Any information can be stored in this property. When the asynchronous operation
		 * completes this information is available to the handling onSuccess or onFailure function.
		 */
		public var data:*;
		
		protected var extraCallbacks:Array;

		/**
		 * Manually trigger the success outcome of this callback.
		 */
		public function doSuccess(result:*=null):void {
			if (onSuccess != null) {
				FlexUtils.callFunc(onSuccess, null, result, data);
			}
			if (extraCallbacks != null) {
				for (var i:int = 0; i < extraCallbacks.length; i++) {
					if (extraCallbacks[i] is Callback) //other IResponders are triggered in result()
						Callback(extraCallbacks[i]).doSuccess(result);
				}
			}
		}
		
		/**
		 * Manually trigger the failure outcome of this callback.
		 */
		public function doFailure(error:Error=null):void {
			if (onFailure != null) {
				FlexUtils.callFunc(onFailure, null, error, data);
			}
			if (extraCallbacks != null) {
				for (var i:int = 0; i < extraCallbacks.length; i++) {
					if (extraCallbacks[i] is Callback) //other IResponders are triggered in fault()
						Callback(extraCallbacks[i]).doFailure(error);
				}
			}
		}
		
		/**
		 * This adds a Callback or other IResponder to the list of responders that is
		 * also called when an outcome of this callback is called. This method returns
		 * the callback from which it was called so that it can be called inline.
		 */
		public function addCallback(callback:IResponder):Callback {
			if (callback != null) {
				if (extraCallbacks == null) {
					extraCallbacks = new Array();
					extraCallbacks[extraCallbacks.length] = callback;
				}
			}
			return this;
		}
		
		/**
		 * Set the data property. This method returns the callback from which
		 * it was called so that it can be called inline.
		 */
		public function withData(data:*):Callback {
			this.data = data;
			return this;
		}

	//IResponder interface
		/**
		 * Trigger the success outcome of this callback.
		 */
		public function result(result:Object):void
		{
			doSuccess(result.result);
			if (extraCallbacks != null) {
				for (var i:int = 0; i < extraCallbacks.length; i++) {
					if (!(extraCallbacks[i] is Callback)) //Callback objects are triggered in doSuccess
						IResponder(extraCallbacks[i]).result(result);
				}
			}
		}
		
		/**
		 * Trigger the failure outcome of this callback.
		 */
		public function fault(info:Object):void
		{
			doFailure(info.fault);
			if (extraCallbacks != null) {
				for (var i:int = 0; i < extraCallbacks.length; i++) {
					if (!(extraCallbacks[i] is Callback)) //Callback objects are triggered in doFailure
						IResponder(extraCallbacks[i]).result(info);
				}
			}
		}
		
	}
}